#pragma once

/*
 * http related variables and functions.
 */

#include <string>
#include <vector>
#include "asio.hpp"
#include "define.hpp"

namespace anet {
	namespace http {
		// get url's ip lists.
		inline bool getUrlIP(const char *url, std::vector<std::string> &vecIP) {
			auto hostAddr = gethostbyname(url);
			if (hostAddr == nullptr) {
				return false;
			}
			
			for (int i = 0;hostAddr->h_addr_list[i] != nullptr;i++) {
				auto tmpAddr = *(struct in_addr*)hostAddr->h_addr_list[i];
				vecIP.push_back(inet_ntoa(tmpAddr));
			}
			return true;
		}

		// get asio endpoints vector.
		inline bool getIP(const std::string &url, 
			std::vector<anet::tcp::asioEndPoint> &vecEndPoints
		) {
			anet::tcp::asioIOContext ioContext;
			asio::ip::tcp::resolver resolver(ioContext);
			asio::ip::tcp::resolver::query query(url, "");
			for (auto i = resolver.resolve(query);
				i != asio::ip::tcp::resolver::iterator();
				++i
			) {
				vecEndPoints.push_back(*i);
			}
			return true;
		}

		// http status
		enum class httpStatus : int {
			OK = 200,                  // OK
			NO_CONTENT = 204,          // no content
			PARTIAL_CONTENT = 206,     // partial content

			BAD_REQUEST = 400,         // bad request
			FORBID = 403,              // forbid request.
			NOT_FIND = 404,            // not find resource

			SERVER_ERROR = 500,        // internal server error
			SERVICE_UNAVAILABLE = 503, // service unavailable
			// add more status here
		};

		// http status description.
		static const char *getStatusDesc(httpStatus id) {
			switch (id) {
			case httpStatus::OK:
				return "OK";

			case httpStatus::BAD_REQUEST:
				return "bad request";

			case httpStatus::FORBID:
				return "forbid request";

			case httpStatus::NO_CONTENT:
				return "no content";

			case httpStatus::PARTIAL_CONTENT:
				return "partial content";

			case httpStatus::NOT_FIND:
				return "not find";

			case httpStatus::SERVER_ERROR:
				return "internal server error";

			case httpStatus::SERVICE_UNAVAILABLE:
				return "service unavailable";

			default:
				return "ERROR";
			}
		}

		// http const variables.
		static const char *gHttpSpaceLine             = "\r\n\r\n";         // http standard line
		static const char *gHttpCRLF                  = "\r\n";             // CRLF
		static const char *gHttpGetMethod             = "GET";              // get op
		static const char *gHttpPostMethod            = "POST";             // post op
		static const char *gHttpContentLenLine        = "Content-Length:";  // content length
		static const char *gHttpContentTypeLine       = "Content-Type:";    // content type
		static const char *gHttpContentTypeJson       = "application/json"; // json format
		static const char *gHttpContentTypeText       = "text/html";        // text format
		static const char *gHttpContentTypeUrlEncoded = "application/x-www-form-urlencoded";
		static const char *gHttpConnectionLine        = "Connection:";      // connection
		static const char *gHttpConnectionKeepalive   = "keep-alive";       // keep alive for connection
		static const char *gHttpConnectionClose       = "close";            // close for connection
		static const char *gHttpCharsetUtf8           = "charset=utf-8";    // utf charset
		static const char *gHttpVersion11             = "HTTP/1.1";         // http1.1 version
		static const char *gHttpVersion10             = "HTTP/1.0";         // http1.0 version
		static const char *gHttpVersion20             = "HTTP/2.0";         // http2.0 version

		// const variables
		const size_t  gHttpStrFindInvalid      = std::string::npos;           // find position return.
		const size_t  gHttpSpaceLineLen        = strlen(gHttpSpaceLine);      // space len
		const size_t  gHttpContentLenLineLen   = strlen(gHttpContentLenLine); // content len line
		const size_t  gHttpCRLFLen             = strlen(gHttpCRLF);           // enter line len

		// http utility.
		inline std::string trimRight(const std::string &strSrc, char c) {
			const char *pEnd = strSrc.c_str() + strSrc.length();
			pEnd--;
			while (*pEnd == c && pEnd >= strSrc.c_str()) {
				pEnd--;
			}
			return std::string(strSrc.c_str(), pEnd + 1);
		}
		inline std::string trimLeft(const std::string &strSrc, char c) {
			const char *pStart = strSrc.c_str();
			while (*pStart == c && (pStart < strSrc.c_str() + strSrc.length())) {
				pStart++;
			}
			return std::string(pStart, strSrc.c_str() + strSrc.length());
		}
		inline void strSplits(const std::string &strData, 
			const std::string &strDelim, 
			std::vector<std::string> &vecData
		) {
			vecData.reserve(5);
			const char *pNext = strData.c_str();
			const char *pPos = nullptr;
			while ((pPos = strstr(pNext, strDelim.c_str())) != nullptr) {
				auto dst = trimLeft(std::string(pNext, pPos - pNext), '\n');
				dst = trimLeft(dst,' ');
				dst = trimRight(dst,' ');
				vecData.push_back(dst);
				pNext = pPos + 1;
			}

			/* push last substring only if not empty*/
			if (pNext) {
				std::string strData = trimLeft(trimRight(pNext, ' '), '\n');
				strData = trimLeft(strData,' ');
				strData = trimRight(strData,' ');
				if (!strData.empty()) { // only not empty.
					vecData.push_back(strData);
				}
			}
		}
	}
}
